package kr.pe.johnkim.checkmileage.mobile.util;

import com.google.android.gcm.server.Constants;
import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.Result;
import com.google.android.gcm.server.Sender;
import javapns.back.PushNotificationManager;
import javapns.back.SSLConnectionHelper;
import javapns.data.Device;
import javapns.data.PayLoad;
import javapns.exceptions.DuplicateDeviceException;
import javapns.exceptions.NullDeviceTokenException;
import javapns.exceptions.NullIdException;
import javapns.exceptions.UnknownDeviceException;
import kr.pe.johnkim.checkmileage.mobile.domain.CheckMileageCertification;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

import org.apache.commons.lang.math.JVMRandom;

import org.springframework.security.providers.encoding.ShaPasswordEncoder;

/**
 * Check Mileage 공통 기능을 구현한 클래스 이다.
 *
 * @author : johnkim <cale9797@gmail.com / Bettersoft Inc,.>
 * @Date : 12. 7. 23
 */
public class CheckMileageCommon {

    // SLF4J logging
    private Logger logger = LoggerFactory.getLogger(CheckMileageCommon.class);

    ShaPasswordEncoder encoder = new ShaPasswordEncoder();

    public static JVMRandom random = new JVMRandom();

    // Google GCM API Key
    private String googleGCMAPIKey;

    // APNS Development Server URL
    private String apnsDevelopmentServerUrl;

    // APNS Production Server URL
    private String apnsProductionServerUrl;

    // APNS Development Server Port
    private String apnsDevelopmentServerPort;

    // APNS Production Server Port
    private String apnsProductionServerPort;

    // APNS Development Certification FilePath
    private String apnsDevelopmentCertificationFilePath;

    // APNS Production Certification FilePath
    private String apnsProductionCertificationFilePath;

    // APNS Development Certification Password
    private String apnsDevelopmentCertificationPassword;

    // APNS Production Certification Password
    private String apnsProductionCertificationPassword;

    /*
        API Key Setter Injection
     */
    public String getGoogleGCMAPIKey() {
        return googleGCMAPIKey;
    }

    public void setGoogleGCMAPIKey(String googleGCMAPIKey) {
        this.googleGCMAPIKey = googleGCMAPIKey;
    }

    public String getApnsDevelopmentServerUrl() {
        return apnsDevelopmentServerUrl;
    }

    public void setApnsDevelopmentServerUrl(String apnsDevelopmentServerUrl) {
        this.apnsDevelopmentServerUrl = apnsDevelopmentServerUrl;
    }

    public String getApnsProductionServerUrl() {
        return apnsProductionServerUrl;
    }

    public void setApnsProductionServerUrl(String apnsProductionServerUrl) {
        this.apnsProductionServerUrl = apnsProductionServerUrl;
    }

    public String getApnsDevelopmentServerPort() {
        return apnsDevelopmentServerPort;
    }

    public void setApnsDevelopmentServerPort(String apnsDevelopmentServerPort) {
        this.apnsDevelopmentServerPort = apnsDevelopmentServerPort;
    }

    public String getApnsProductionServerPort() {
        return apnsProductionServerPort;
    }

    public void setApnsProductionServerPort(String apnsProductionServerPort) {
        this.apnsProductionServerPort = apnsProductionServerPort;
    }

    public String getApnsDevelopmentCertificationFilePath() {
        return apnsDevelopmentCertificationFilePath;
    }

    public void setApnsDevelopmentCertificationFilePath(String apnsDevelopmentCertificationFilePath) {
        this.apnsDevelopmentCertificationFilePath = apnsDevelopmentCertificationFilePath;
    }

    public String getApnsProductionCertificationFilePath() {
        return apnsProductionCertificationFilePath;
    }

    public void setApnsProductionCertificationFilePath(String apnsProductionCertificationFilePath) {
        this.apnsProductionCertificationFilePath = apnsProductionCertificationFilePath;
    }

    public String getApnsDevelopmentCertificationPassword() {
        return apnsDevelopmentCertificationPassword;
    }

    public void setApnsDevelopmentCertificationPassword(String apnsDevelopmentCertificationPassword) {
        this.apnsDevelopmentCertificationPassword = apnsDevelopmentCertificationPassword;
    }

    public String getApnsProductionCertificationPassword() {
        return apnsProductionCertificationPassword;
    }

    public void setApnsProductionCertificationPassword(String apnsProductionCertificationPassword) {
        this.apnsProductionCertificationPassword = apnsProductionCertificationPassword;
    }

    /**
     * 입력 받은 패스워드를 SHA 를 이용 해서 암호화 한 뒤 반환 한다.
     *
     * @param password
     * @return
     */
    public String getEncodedPassword(String password) {
        logger.info("getEncodedPassword");
        return encoder.encodePassword(password, null);
    }

    /**
     * 사용자의 Mobile Device 로 Google GCM 을 발송 한다.
     *
     * Used for Android Device.
     *
     * @param registrationId
     * @param messageContent
     * @throws IOException
     */
    public void sendASMessage(String registrationId, String messageContent) throws IOException {
        logger.info("sendASMessage");

        logger.info("API Key          : {}", this.getGoogleGCMAPIKey());
        logger.info("Registration Id  : {}", registrationId);
        logger.info("Message Contents : {}", messageContent);

        Sender sender = new Sender(this.getGoogleGCMAPIKey());
        Message message = new Message.Builder().addData("MESSAGE", messageContent).build();
        Result result = sender.send(message, registrationId, 2);

        if(result.getMessageId() != null) {
            String canonicalRegId = result.getCanonicalRegistrationId();
            if(canonicalRegId != null) {
                //
                logger.info("canonicalRegId   : {}", canonicalRegId);
            }
        } else {
            String error = result.getErrorCodeName();

            logger.info("error            : {}", error);

            if(error.equals(Constants.ERROR_NOT_REGISTERED)) {
                //
            }
        }
    }

    /**
     * 사용자의 Mobile Device 로 APNS 메시지를 발송 한다.
     *
     * Used for iOS Device.
     *
     * @param registrationId
     * @param messageContent
     * @throws Exception
     * @throws JSONException
     * @throws DuplicateDeviceException
     * @throws NullDeviceTokenException
     * @throws NullIdException
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws UnrecoverableKeyException
     * @throws UnknownDeviceException
     */
    public void sendIPMessage(String registrationId, String messageContent) throws Exception,
            JSONException, DuplicateDeviceException, NullDeviceTokenException, NullIdException,
            NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException,
            CertificateException, UnrecoverableKeyException, UnknownDeviceException {
        logger.info("sendIPMessage");

        PayLoad payLoad = new PayLoad();
        payLoad.addAlert(messageContent);
        payLoad.addBadge(1);
        payLoad.addSound("default");

        PushNotificationManager pushNotificationManager = PushNotificationManager.getInstance();
        pushNotificationManager.addDevice("iPhone", registrationId);

        /**
         * Apple Store 에 배포 후  Development 정보를 Production 로 수정 한다.
         * */

        logger.info("Server Url             : {}", this.getApnsProductionServerUrl());
        logger.info("Server Port            : {}", this.getApnsProductionServerPort());
//        logger.info("Certification FilePath : {}", this.getApnsProductionCertificationFilePath());
//        logger.info("Certification Password : {}", this.getApnsProductionCertificationPassword());

        pushNotificationManager.initializeConnection(
                this.getApnsProductionServerUrl(),
                Integer.parseInt(this.getApnsProductionServerPort()),
                this.getApnsProductionCertificationFilePath(),
                this.getApnsProductionCertificationPassword(),
                SSLConnectionHelper.KEYSTORE_TYPE_PKCS12
        );

        // push 를 날린다.
        Device client = pushNotificationManager.getDevice("iPhone");
        pushNotificationManager.sendNotification(client, payLoad);
        pushNotificationManager.stopConnection();

        pushNotificationManager.removeDevice("iPhone");
    }

    /**
     * 테스트용.
     *
     * 사용자의 Mobile Device 로 APNS 메시지를 발송 한다.
     *
     * iPhone.
     *
     * @param registrationId
     * @param messageContent
     * @throws Exception
     * @throws JSONException
     * @throws DuplicateDeviceException
     * @throws NullDeviceTokenException
     * @throws NullIdException
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws UnrecoverableKeyException
     * @throws UnknownDeviceException
     */
    public void testSendIPMessage(String registrationId, String messageContent) throws Exception,
            JSONException, DuplicateDeviceException, NullDeviceTokenException, NullIdException,
            NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException,
            CertificateException, UnrecoverableKeyException, UnknownDeviceException {
        logger.info("testSendIPMessage");

        PayLoad payLoad = new PayLoad();
        payLoad.addAlert(messageContent);
        payLoad.addBadge(1);
        payLoad.addSound("default");

        PushNotificationManager pushNotificationManager = PushNotificationManager.getInstance();
        pushNotificationManager.addDevice("iPhone", registrationId);

        /**
         * Apple Store 에 배포 후  Development 정보를 Production 로 수정 한다.
         * */

        logger.info("Server Url             : {}", this.getApnsDevelopmentServerUrl());
        logger.info("Server Port            : {}", this.getApnsDevelopmentServerPort());
//        logger.info("Certification FilePath : {}", this.getApnsDevelopmentCertificationFilePath());
//        logger.info("Certification Password : {}", this.getApnsDevelopmentCertificationPassword());

        pushNotificationManager.initializeConnection(
                this.getApnsDevelopmentServerUrl(),
                Integer.parseInt(this.getApnsDevelopmentServerPort()),
                this.getApnsDevelopmentCertificationFilePath(),
                this.getApnsDevelopmentCertificationPassword(),
                SSLConnectionHelper.KEYSTORE_TYPE_PKCS12
        );

        // push 를 날린다.
        Device client = pushNotificationManager.getDevice("iPhone");
        pushNotificationManager.sendNotification(client, payLoad);
        pushNotificationManager.stopConnection();

        pushNotificationManager.removeDevice("iPhone");
    }

    /**
     * 인증 번호를 넘겨 준다.
     *
     * 고유한 UUID 를 생성한 뒤에 앞 4자리를 잘라서 넘겨 준다.
     *
     * @return
     */
    public String getCertificationNumber() {
        logger.info("getCertificationNumber");
        String uuid = String.valueOf(random.nextInt(999999999));

        return uuid.substring(0, 4);
    }

    /**
     * 현재 날짜와 시간을 2010-10-22 15:20:46 포멧의 string 으로 반환한다.
     *
     * @return
     */
    public String getCurrentDate() {
        logger.info("getCurrentDate");

        String currentDate = "";

        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        currentDate = simpleDateFormat.format(date);

        return currentDate;
    }

    /**
     * '-' 없이 연결된 전화번호를 '-' 를 사용해서 구별해서 반환한다.
     *
     * @param phoneNumber
     * @return
     */
    public String getSMSPhoneNumber(String phoneNumber) {

        String result = "";
        int phoneNumberLength = phoneNumber.length();

        if(phoneNumberLength == 10) {
            // 3자리 국번호가 '-' 가 없을때.
            String temp1 = phoneNumber.substring(0, 3);
            String temp2 = phoneNumber.substring(3, 6);
            String temp3 = phoneNumber.substring(6, 10);
            result = temp1 + "-" + temp2 + "-" + temp3;
        } else if(phoneNumberLength == 11) {
            // 4자리 국번호가 '-' 가 없을때.
            String temp1 = phoneNumber.substring(0, 3);
            String temp2 = phoneNumber.substring(3, 7);
            String temp3 = phoneNumber.substring(7, 11);
            result = temp1 + "-" + temp2 + "-" + temp3;
        } else if(phoneNumberLength == 12) {
            // 3자리 국번호가 '-' 가 있을때.
        } else if(phoneNumberLength == 13) {
            // 4자리 국번호가 '-' 가 있을때.
        }

        return result;
    }

    /**
     * Null check.
     *
     * 인증번호 전송할때 사용한다.
     *
     * @param str
     * @param Defaultvalue
     * @return
     * @throws Exception
     */
    public static String nullcheck(String str,  String Defaultvalue ) throws Exception {

        String ReturnDefault = "" ;

        if (str == null) {
            ReturnDefault =  Defaultvalue ;
        } else if (str == "" ) {
            ReturnDefault =  Defaultvalue ;
        } else {
            ReturnDefault = str ;
        }

        return ReturnDefault ;
    }

    /**
     * Base64 Encoder.
     *
     * 인증번호 전송할때 사용한다.
     *
     * @param str
     * @return
     * @throws java.io.IOException
     */
    public static String base64Encode(String str)  throws java.io.IOException {
        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        byte[] strByte = str.getBytes();
        String result = encoder.encode(strByte);

        return result ;

    }

    /**
     * Base64 Decoder.
     *
     * 인증번호 전송할때 사용한다.
     *
     * @param str
     * @return
     * @throws java.io.IOException
     */
    public static String base64Decode(String str)  throws java.io.IOException {
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        byte[] strByte = decoder.decodeBuffer(str);
        String result = new String(strByte);

        return result ;

    }

    /**
     * cafe24 API 를 사용해서 SMS 를 발송한다.
     *
     * 1. sms_url(전송요청 url) : http://sslsms.cafe24.com/sms_sender.php
     * 2. user_id(아이디)      : better3801
     * 3. secure(인증키)       : eb174f728a17edbd56447225c8754fb7
     *
     * @param checkMileageCertification
     * @return
     * @throws Exception
     */
    public CheckMileageCertification sendSMS(CheckMileageCertification checkMileageCertification) throws Exception {
        logger.info("sendSMS");

        String charsetType = "UTF-8"; //EUC-KR 또는 UTF-8

        String sms_url = "http://sslsms.cafe24.com/sms_sender.php"; // SMS 전송요청 URL

        String user_id = base64Encode("better3801"); // SMS아이디
        String secure = base64Encode("eb174f728a17edbd56447225c8754fb7");//인증키
        String msg = base64Encode(checkMileageCertification.getMsg());
        String rphone = base64Encode(checkMileageCertification.getRphone());
        String sphone1 = base64Encode(checkMileageCertification.getSphone1());
        String sphone2 = base64Encode(checkMileageCertification.getSphone2());
        String sphone3 = base64Encode(checkMileageCertification.getSphone3());
        String rdate = base64Encode(checkMileageCertification.getRdate());
        String rtime = base64Encode(checkMileageCertification.getRtime());
        String mode = base64Encode("1");
        String testflag = base64Encode(checkMileageCertification.getTestflag());
        String destination = base64Encode(checkMileageCertification.getDestination());
        String repeatFlag = base64Encode(checkMileageCertification.getRepeatFlag());
        String repeatNum = base64Encode(checkMileageCertification.getRepeatNum());
        String repeatTime = base64Encode(checkMileageCertification.getRepeatTime());
        String returnurl = "http://better3801.cafe24.com";
        String nointeractive = checkMileageCertification.getNointeractive();

        String[] host_info = sms_url.split("/");
        String host = host_info[2];
        String path = "/" + host_info[3];
        int port = 80;

        String arrKey[]
                = new String[] {"user_id","secure","msg", "rphone","sphone1","sphone2","sphone3","rdate","rtime"
                ,"mode","testflag","destination","repeatFlag","repeatNum", "repeatTime"};
        String valKey[]= new String[arrKey.length] ;
        valKey[0] = user_id;
        valKey[1] = secure;
        valKey[2] = msg;
        valKey[3] = rphone;
        valKey[4] = sphone1;
        valKey[5] = sphone2;
        valKey[6] = sphone3;
        valKey[7] = rdate;
        valKey[8] = rtime;
        valKey[9] = mode;
        valKey[10] = testflag;
        valKey[11] = destination;
        valKey[12] = repeatFlag;
        valKey[13] = repeatNum;
        valKey[14] = repeatTime;

        String boundary = "";
        Random rnd = new Random();
        String rndKey = Integer.toString(rnd.nextInt(32000));
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] bytData = rndKey.getBytes();
        md.update(bytData);
        byte[] digest = md.digest();

        for(int i =0;i<digest.length;i++) {
            boundary = boundary + Integer.toHexString(digest[i] & 0xFF);
        }

        boundary = "---------------------"+boundary.substring(0,10);

        // 본문 생성
        String data = "";
        String index = "";
        String value = "";

        for (int i=0;i<arrKey.length; i++) {
            index =  arrKey[i];
            value = valKey[i];
            data +="--"+boundary+"\r\n";
            data += "Content-Disposition: form-data; name=\""+index+"\"\r\n";
            data += "\r\n"+value+"\r\n";
            data +="--"+boundary+"\r\n";
        }

        //out.println(data);

        InetAddress addr = InetAddress.getByName(host);
        Socket socket = new Socket(host, port);

        // 헤더 전송
        BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), charsetType));
        wr.write("POST "+path+" HTTP/1.0\r\n");
        wr.write("Content-Length: "+data.length()+"\r\n");
        wr.write("Content-type: multipart/form-data, boundary="+boundary+"\r\n");
        wr.write("\r\n");

        // 데이터 전송
        wr.write(data);
        wr.flush();

        // 결과값 얻기
        BufferedReader rd = new BufferedReader(new InputStreamReader(socket.getInputStream(),charsetType));
        String line;
        String alert = "";
        ArrayList tmpArr = new ArrayList();

        while ((line = rd.readLine()) != null) {
            tmpArr.add(line);
        }

        wr.close();
        rd.close();

        String tmpMsg = (String)tmpArr.get(tmpArr.size()-1);
        String[] rMsg = tmpMsg.split(",");
        String Result= rMsg[0]; //발송결과
        String Count= ""; //잔여건수

        if(rMsg.length>1) {
            Count= rMsg[1];
        }

        // 발송결과 알림.

        CheckMileageCertification result = new CheckMileageCertification();

        if(Result.equals("success")) {
            result.setResult("success to send SMS.");
        } else if(Result.equals("3205")) {
            result.setResult("fail. wrong phone number format.");
        }

        return result;
    }
}
